package com.ruoyi.mq

import cn.hutool.core.date.DateUtil
import cn.hutool.core.util.StrUtil
import cn.hutool.json.JSONObject
import cn.hutool.json.JSONUtil
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import com.ruoyi.app.common.HuoshanApi
import com.ruoyi.app.common.NetUrlUploadOss
import com.ruoyi.app.common.VideoChangeFaceParam
import com.ruoyi.app.common.videoCheckResult
import com.ruoyi.app.domain.dto.VideoChangeFaceDto
import com.ruoyi.back.domain.ChannelFreeTemplate
import com.ruoyi.back.domain.ChannelManager
import com.ruoyi.back.domain.UserRecord
import com.ruoyi.back.domain.v4.ContentSourceCheck
import com.ruoyi.back.mapper.ChangeFaceApiDetailMapper
import com.ruoyi.back.mapper.ChannelFreeTemplateMapper
import com.ruoyi.back.mapper.ContentSourceCheckMapper
import com.ruoyi.back.service.AppUserService
import com.ruoyi.back.service.ChannelManagerService
import com.ruoyi.back.service.TemplateService
import com.ruoyi.back.service.UserRecordService
import com.ruoyi.back.v1.domain.ChangeFaceApiDetail
import com.ruoyi.exception.FlyException
import com.ruoyi.file.router.FileCompressService
import com.ruoyi.util.changeVideoFace
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.io.IOException
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ThreadPoolExecutor

interface ChangeFaceExtendService {

    /**
     * 成功处理
     */
    fun success(userRecord: UserRecord, data: JSONObject? = null, type: Int)

    /**
     * 异常处理
     */
    fun error(userRecord: UserRecord, data: JSONObject?, errorMsg: String?)

    /**
     * 审核结果
     */
    fun checkTask(userRecord: UserRecord): Boolean

}


@Service
class ChangeFaceExtendServiceImpl : ChangeFaceExtendService {

    private val log: Logger = LoggerFactory.getLogger(ChangeFaceTaskManager::class.java)

    @Autowired
    private lateinit var fileCompressService: FileCompressService

    @Autowired
    private lateinit var changeFaceTask: ThreadPoolExecutor

    @Autowired
    private lateinit var userRecordService: UserRecordService

    @Autowired
    private lateinit var huoshanApi: HuoshanApi

    @Autowired
    private lateinit var appUserService: AppUserService

    @Autowired
    private lateinit var channelFreeTemplateMapper: ChannelFreeTemplateMapper

    @Autowired
    private lateinit var channelManagerService: ChannelManagerService

    @Autowired
    private lateinit var netUrlUploadOss: NetUrlUploadOss

    @Autowired
    private lateinit var changeFaceApiDetailMapper: ChangeFaceApiDetailMapper

    @Autowired
    private lateinit var contentSourceCheckMapper: ContentSourceCheckMapper

    @Autowired
    private lateinit var templateService: TemplateService

    @Autowired
    private lateinit var changeFaceTaskManager: ChangeFaceTaskManager

    /**
     * 压缩视频
     */
    fun UserRecord.compress(errorMsg: String? = null) = CompletableFuture.runAsync({

        errorMsg?.run {
            log.info("出现视频不符合规则, 开始重试:{}", this)
        }

        JSONUtil.toBean(param, VideoChangeFaceDto::class.java)?.let { videoChangeFaceDto ->

            videoChangeFaceDto.localTemplateUrl?.let { localTemplateUrl ->
                val compressUrl = fileCompressService.compress(localTemplateUrl)
                log.info("转换后的 url: -> $compressUrl")

                if (userRecordService.updateById(this.apply {
                        requestId =  //调用火山api
                            huoshanApi.videoSwapFace(VideoChangeFaceParam().apply {
                                faceUrl = videoChangeFaceDto.referenceUrl
                                videoUrl = compressUrl
                                logo = videoChangeFaceDto.aiWatermark
                            })
                    })) {
                    this.id?.run {
                        changeFaceTaskManager.sendMq(this)
                    }
                }
            }

        }

    }, changeFaceTask).join()


    /**
     * 增加接口调用详情
     */
    fun addApiDetail(userRecord: UserRecord) = userRecord.run {

        //增加接口调用详情
        changeFaceApiDetailMapper.insert(
            ChangeFaceApiDetail().apply {
                changeFaceType = 2
                changeFaceApiType = apiType
                totalDuration = videoDuration?.toLong()
                DateUtil.date().let {
                    year = DateUtil.year(it)
                    month = DateUtil.month(it) + 1
                    day = DateUtil.dayOfMonth(it)
                }
                userId = createBy
            })
    }

    /**
     * 普通异常
     */
    fun UserRecord.commonException(errorMsg: String) {

        if (userRecordService.updateById(apply {
                this.status = 3
                completeTime = Date()
                this.errorMsg = errorMsg
            })) {

            JSONUtil.parseObj(param).let { param ->

                //返还用户积分
                appUserService.updateById(appUserService.getById(createBy)?.apply {

                    if ((param.getBool("pay") == true).not() && StrUtil.isNotBlank(param.getStr("templateId"))
                        && channelFreeTemplateMapper.selectOne(
                            KtQueryWrapper(ChannelFreeTemplate::class.java)
                                .eq(
                                    ChannelFreeTemplate::channelId, channelManagerService.getOne(
                                        KtQueryWrapper(ChannelManager::class.java)
                                            .eq(
                                                ChannelManager::code,
                                                param.getStr("channelCode")
                                            )
                                    )?.id
                                )
                                .eq(ChannelFreeTemplate::templateId, param.getStr("templateId"))
                                .eq(ChannelFreeTemplate::templateType, 1)
                        ) == null
                    ) {
                        changeFaceRemainCount = (changeFaceRemainCount ?: 0) - 1
                    }
                })
            }
        }

    }

    /**
     * 成功方法
     */
    override fun success(userRecord: UserRecord, data: JSONObject?, type: Int): Unit = userRecord.run {
        userRecordService.updateById(apply {
            try {
                this.status = 2
                completeTime = Date()
                this.url = netUrlUploadOss.upload(
                    when (type) {
                        1 -> data?.getStr("videoUrl") ?: throw FlyException("获取视频资源失败!")
                        2 -> data?.getStr("video_url") ?: throw FlyException("获取视频资源失败!")
                        else -> throw FlyException("不支持的接口类型!")
                    }, "mp4"
                )
                //增加接口调用详情
                addApiDetail(this)

                //修改单次支付状态
                appUserService.getById(createBy)?.run {
                    appUserService.updateById(apply {
                        singleVideoPay = false
                    })
                }
            } catch (e: Exception) {
                if (e is IOException) {
                    retry(this, e)
                } else {
                    error(this, data, e.message)
                }
            }
        })
    }

    /**
     * 异常处理
     */
    override fun error(userRecord: UserRecord, data: JSONObject?, errorMsg: String?): Unit = userRecord.run {

        "${errorMsg ?: data?.getStr("ErrorMessage")}".also { log.info("视频换脸发生异常 $it") }
            .let { errorMsg ->

                when (userRecord.apiType) {
                    1 -> {
                        val result = JSONUtil.parseObj(data)
                        if (result.getStr("ErrorCode") == "InvalidParameter.TooLarge") {
                            compress(result.getStr("ErrorMessage"))
                        } else {
                            commonException(errorMsg)
                        }
                    }

                    else -> {
                        if (errorMsg != "资源找不到!") {
                            val result = JSONUtil.parseObj(errorMsg)
                            if (result.getInt("code") == 50200 && result.getStr("message").contains("fps")) {
                                compress(errorMsg)
                            } else if (result.getInt("code") == 50211 || result.getInt("code") == 50214) {
                                compress(errorMsg)
                            } else {
                                commonException(errorMsg)
                            }

                        } else {
                            commonException("资源缺失(相关积分已经返还), 请重试!")
                        }
                    }
                }
            }
    }


    override fun checkTask(userRecord: UserRecord): Boolean {
        //检验审核结果
        userRecord.checkTaskId?.run {
            videoCheckResult(this).let { videoCheckResults ->
                if (videoCheckResults.first().status == 1) {
                    userRecord.id?.run {
                        changeFaceTaskManager.sendMq(this)
                    }
                    return false
                } else {
                    if (videoCheckResults.first().pass == true) {
                        contentSourceCheckMapper.selectOne(
                            KtQueryWrapper(ContentSourceCheck::class.java)
                                .eq(ContentSourceCheck::taskId, userRecord.checkTaskId)
                        )
                            ?.run {
                                contentSourceCheckMapper.updateById(this.apply {
                                    checkStatus = 2
                                })
                            }
                        return true
                    } else {
                        contentSourceCheckMapper.selectOne(
                            KtQueryWrapper(ContentSourceCheck::class.java)
                                .eq(ContentSourceCheck::taskId, userRecord.checkTaskId)
                        )
                            ?.run {
                                contentSourceCheckMapper.updateById(this.apply {
                                    checkStatus = 3
                                    text = videoCheckResults.map { it.errorMsg }
                                        .joinToString(",")
                                })
                            }

                        userRecordService.updateById(userRecord.apply {
                            status = 3
                            errorMsg = videoCheckResults.map { it.errorMsg }
                                .joinToString(",")
                        })

                        return false
                    }
                }
            }
        }

        return true
    }

    /**
     * 重试
     */
    fun retry(userRecord: UserRecord, e: IOException) = userRecord.run {

        log.error("出现403错误, 开始重试:{}", e.message)

        try {
            JSONUtil.toBean(param, VideoChangeFaceDto::class.java)?.let { videoChangeFaceDto ->
                if (userRecordService.updateById(this.apply {
                        requestId = videoChangeFaceDto.templateId?.let { templateId ->
                            changeVideoFace(
                                videoChangeFaceDto,
                                templateService.getById(templateId).videoTemplateId,
                                templateService.getById(templateId).content
                            )
                        } ?: run {
                            changeVideoFace(
                                videoChangeFaceDto,
                                null,
                                videoChangeFaceDto.localTemplateUrl
                            )
                        }
                    })) {
                    id?.run {
                        changeFaceTaskManager.sendMq(this)
                    }
                }
            }
        } catch (e: Exception) {
            error(this, null, "当前请求参数为空, 请求结束(积分已经返还 请重试)")
        }
    }
}
